////////////////////////////////////////////////////////////////////
//	
//	File: Connect4Game.chpl 
//	Team: Patrick Copeland, Deanna Fink, Jordan Hildebrandt
//	Date: 15 February 2012
//	Time: 16:40
//	Desc: Connect4Game.chpl defines a "referee" for the 
//		  game Connect4.  It keeps track of the current 
//		  players' turn, prompts them for their move and 
//		  determines when the game has finished.
//
////////////////////////////////////////////////////////////////////


use Vector, Player;

class Connect4Game {
	
	// ====================
	// instance variables
	// ====================
	
	/**
	 * Number of columns on the board 
	 */
	var numColumns:int;
	
	/**
	 * Number of rows on the board
	 */
	var numRows:int;
	
	
	var D_row:domain(1);

	var D_column:domain(1);

	var lastMove:int;

//	/**
//	 * (column x row) matrix as the board
//	 */
	var board:[D_row][D_column] string;
	

	//====================
	// public methods
	// ====================

	/**
	 * Class constructor.
	 */
	proc Connect4Game(numRows:int, numColumns:int) {
		this.numRows = numRows;
		this.numColumns = numColumns;
		this.lastMove = 0;
		
		this.D_row = [0..this.numRows-1];
		this.D_column = [0..this.numColumns-1];
		
		//var board: [0..numRows-1][0..numColumns-1] string;
		
		forall row in 0..numRows-1 {
			forall column in 0..numColumns-1 {
				this.board[row][column] = "_";
			}
		}
	}
	
	proc Connect4Game(game:Connect4Game) {
		this.numColumns = game.numColumns;
		this.numRows = game.numRows;
		this.D_row = game.D_row;
		this.D_column = game.D_column;
		this.board = game.board;
		this.lastMove = game.lastMove;
	}
	
	/**
	 * Returns whether the move was within the bounds of the board and
	 * there is a blank space available in given row
	 */
	proc isValidMove(column:int) {
		return column >= 0 && column < this.numColumns && this.board[0][column] == "_";
	}
	
	/**
	 * Checks whether this game has any children.
	 */
	proc hasChildren() {
		if this.isOver() {							// the game is over
			return false;
		} else {
			for column in 0..numColumns-1 {			// there is atleast one open space
				if board[0][column] == "_" {
						return true;
				}
			}
		}
		
		return false;								// no open spaces
	}
	
	/**
	 * Get the child game for a given move column and player
	 * 
	 * @param column Column that the move is made in.
	 * @param player Player make the move.
	 * 
	 * @return Returns a new Connect4Game with the move made.
	 */
	proc getChild(column:int, symbol:string) : Connect4Game {
		// if this.isValidMove(column)
		
		var newGame:Connect4Game;
		newGame = new Connect4Game(this);
		
		var rowIndex:int;
		rowIndex = this.lowestRowInColumn(column);

		newGame.board[rowIndex][column] = symbol;
		newGame.lastMove = column;
		
		return newGame;
	}
	
	proc getBoard(){
		return this.board;
	}
	
	/**
	 * Returns all possible children for a given game state.
	 * 
	 * @param player Player that is making moves.
	 * 
	 * @return Returns a vector of all possible children from a given game state.
	 */
	proc getChildren(symbol:string) {
		var childrenVector:Vector(Connect4Game);
		childrenVector = new Vector(Connect4Game, this.numColumns+5);
		
		for column in 0..this.numColumns-1 {
			if this.isValidMove(column) {
				childrenVector.add(this.getChild(column, symbol));
			}
		}
		
		return childrenVector;
	}
	
	
	/**
	 * Finds the lowest row in the given column.
	 * 
	 * @param column Column to search.
	 */
	proc lowestRowInColumn(column:int) {
		for row in 0..this.numRows-1 {
			if board[row][column] == "x" || board[row][column] == "o" {
					return row - 1;
			}
		}
		
		return this.numRows-1;
	}
	
	/**
	 * Checks whether the game is over.
	 */
	proc isOver() {
		var basePiece:string;
		var seq = 1;
		
		// check horizontally
		for row in 0..this.numRows-1 {
			for column in 0..this.numColumns-4 {
				basePiece = this.board[row][column];
				
				if basePiece != "_" {
					while basePiece == this.board[row][column+seq] {		// column moves right
						seq = seq + 1;
						
						if seq == 4 {			// found a sequence of 4
							return true;		// game over
						}
					}
				}
			}
		}
		
		
		// check vertically
		for row in 0..this.numRows-4 {
			for column in 0..this.numColumns-1 {
				basePiece = this.board[row][column];
				
				if basePiece != "_" {
					seq = 1;
					while basePiece == this.board[row+seq][column] {		// row moves "down"
						seq = seq + 1;
						
						if seq == 4 {			// found a sequence of 4
							return true;		// game over
						}
					}
				}
			}
		}
		
		
		// check down diagonal
		for row in 0..this.numRows-4 {
			for column in 0..this.numColumns-4{
				basePiece = this.board[row][column];
				
				if basePiece != "_" {
					seq = 1;
					while basePiece == this.board[row+seq][column+seq] {	// row moves "down"
						seq = seq + 1;										// column moves right"
						
						if seq == 4 {			// found a sequence of 4
							return true;		// game over
						}
					}
				}
			}
		}
		
		// check up diagonal
		for row in 3..this.numRows-1 {
			for column in 0..this.numColumns-4 {
				basePiece = this.board[row][column];
				
				if basePiece != "_" {
					seq = 1;
					while basePiece == this.board[row-seq][column+seq] {	// row moves "up"
						seq = seq + 1;										// column moves "right"
						
						if seq == 4 {			// found a sequence of 4
							return true;		// game over
						}
					}
				}
			}
		}
		
		/*
		// check if any blank spaces left
		for column in 0..numColumns-1 {
			if this.board[0][column] == "_" {
					return false;
			}
		}
		
		return true;		// no seq of 4 was found
							// but there are no more spaces
							// TODO: how to do cat game?
		*/
		
		return false;
	}
	
	proc isNSeqHoriz(n, state, row, col, symbol) {
		for i in [0..n-1] {
			if state[row][i] != symbol {
				return false;
			}
		}
	   
	   return true;
	}

	proc isNSeqVert(n, state, row, col, symbol) {
		for i in [0..n-1] {
			if state[i][col] != symbol {
				return false;
			}
		}
	   
		return true;
	}

	proc isNSeqUpDiag(n, state, row, col, symbol) {
		for i in [0..n-1] {
			if state[row-i][col+i] != symbol {
				return false;
			}	
		}
	   
	   return true;
	}

	proc isNSeqDownDiag(n, state, row, col, symbol) {
		for i in [0..n-1] {
			if state[row+i][col+i] != symbol {
				return false;
			}
		}
	   
	   return true;
	}
	
}

// end Connect4Game.chpl